/**
 * 工具包头文件
 */

#pragma once

#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "std_err.h"

#ifndef BOOL
#define BOOL int
#endif

#define MAXU32 	((u32)0xFFFFFFFF)
#define MAXU16	((u16)0xFFFF)

/// u16 字节序颠倒
#define BL16(w)  		((((w)&0x00FF)<<8) | (((w)&0xFF00)>>8))
/// u32 字节序颠倒
#define BL32(dw) 		((((dw)&0x000000FF)<<24) | (((dw)&0x0000FF00)<<8) | (((dw)&0x00FF0000)>>8) | (((dw)&0xFF000000)>>24))
/// 第 n 位的掩码
#define BV(n) 			(1U<<(n))
/// 取 v 的第 n 位的值
#define BITx(v, n)		(((v) >> (n)) & 1)
/// 取一个字节数组的第 n 位的值
#define BITn(buff, n)	BITx((buff)[(n)>>3], (n)&7)
/// 设置 v 的第 n 位为 1
#define BitSet(v, n)	((v) |= BV(n))
/// 设置 v 的第 n 位为 0
#define BitReset(v, n)	((v) &= (~BV(n)))
/// 把 v 的第 n 位取反
#define BitInvert(v, n)	((v) ^= BV(n))

/// 取字节的高 4 位
#define HI4B(b)   (((b)&0xF0) >>4)
/// 取字节的低 4 位
#define LO4B(b)   ((b)&0x0F)

/// 取 u16 的高字节
#define HIBYTE(w)   (((w)&0xFF00) >>8)
/// 取 u16 的低字节
#define LOBYTE(w)   ((w)&0x00FF)

/// 取 u32 的高 u16
#define HIWORD(dw)  (((dw)&0xFFFF0000)>>16)
/// 取 u32 的低 u16
#define LOWORD(dw)  ((dw)&0x0000FFFF)

/// 取 u32 的第 1 字节(大端在前)
#define BHH(dw) HIBYTE(HIWORD(dw))
/// 取 u32 的第 2 字节(大端在前)
#define BHL(dw) LOBYTE(HIWORD(dw))
/// 取 u32 的第 3 字节(大端在前)
#define BLH(dw) HIBYTE(LOWORD(dw))
/// 取 u32 的第 4 字节(大端在前)
#define BLL(dw) LOBYTE(LOWORD(dw))

#define MAKE_BYTE(H4B, L4B)				((u8)((((H4B)<<4)&0xF0) | ((L4B)&0x0F)))
#define MAKE_WORD(BH, BL)               ((u16)((((BH)&0xFF)<<8) | ((BL)&0xFF)))
#define MAKE_DWORD(BHH, BHL, BLH, BLL)  ((u32)((MAKE_WORD((BHH), (BHL))<<16) | MAKE_WORD((BLH), (BLL))))

/// 生成一个表示日期的16位整数：年:7-月:4-日:5，表示 2000/1/1 ~ 2127/12/31 之间的所有日期
#define MAKE_DATE16(y, m, d)            ((u16)((((y)%1000)<<9) | ((m)<<5) | (d)))

/// 生成版本号字符串
#define MAKE_VERSION_STRING(major, minor, build)  (QUOTE(major) "." QUOTE(minor) "." QUOTE(build))

/// 绝对值
#define ABS(num) (((num)>=0)?(num):-(num))
/// 取正负符号(1表示正数，-1表示负数)
#define SIGN(num) (((num)>=0)?(1):(-1))
/// 整数4舍5入除法
#define IDIV45(a, b) (((a)+((b)>>1))/(b))
/// 浮点数4舍5入除法
#define FDIV45(a, b) ((a)*1.0/(b)+0.5)
/// 求差值(绝对值)
#define DIFF(a, b) (((a)>(b))?((a)-(b)):((b)-(a)))
/// 判断两个浮点数是否近似相等
#define FEQ(f1, f2) ((f1 >= f2) ? ((f1 - f2) <= 0.000001) : ((f2 - f1) <= 0.000001))

/// num1, num2 之前的误差是否小于 err
#define ISABOUT(num1, num2, err) 	((((num1)-(num2))>(err) || ((num1)-(num2))<(-(err)))?false:true)
/// num 是否在 [min, max] 之间
#define ISBETWEEN(num, min, max) 	(((num)>=(min) && ((num)<=(max)))?true:false)
/// num1 和 num2 相差多少(%)
#define ISTIMES(num1, num2) 		((num1>num2)?(num1*100L/num2):(num2*100L/num1))
/// num1 和 num2 误差是多少(%)
#define PERR(num1, num2)			(ISTIMES(num1, num2) - 100)

#ifndef MIN
#define MIN(a, b) ((a)<(b)?(a):(b))
#endif

#ifndef MAX
#define MAX(a, b) ((a)>(b)?(a):(b))
#endif

/// 取3个数的最大值
#define MIN3(a, b, c) MIN(MIN((a), (b)), (c))
#define MAX3(a, b, c) MAX(MAX((a), (b)), (c))

/// 计算一个数组中元素的数量
#define COUNT(v) (sizeof(v)/sizeof(v[0]))

/// 是否是有效的 HEX 字符
#define IS_HEX_CHAR(ch) ( ((ch>='0') && (ch<='9')) || ((ch>='A') && (ch<='F')) || ((ch>='a') && (ch<='f')) )

/// 无操作
#ifdef __cplusplus
#define NULLOP()    ((void*)0)
#else
//#define NULLOP()    ((void *)0)
#define NULLOP()    do {} while(0)
#endif

/// 避免未引用警告
#define UNUSED(x)	((void *)(x))

/// 多行语句宏定义
#define st(x)      do { x } while (__LINE__ == -1)

/// 引用
#define QUOTE(x) #x

#ifndef FIELD_OFFSET
#define FIELD_OFFSET(type, field) ((size_t)&(((type*)0)->field))
#endif

// 分配指定大小的内存，并把它清零
static inline void* malloc0(size_t size) { void* mem = malloc(size); return mem ? memset(mem, 0, size) : NULL; }
// 分配指定类型的内存
#define MALLOC(t)	((t*)malloc(sizeof(t)))
// 分配指定类型的内存，并把它清零
#define MALLOC0(t)	((t*)malloc0(sizeof(t)))

/**
 * 设置调试标志位
 * @param 第几个标志位。应小于 size_t 的位数。
 * @param 设置为 true 或 false
 */
void _dbgflag_set(unsigned int idx, bool val);
/**
 * 获取调试标志位
 * @param 第几个标志位。应小于 size_t 的位数。
 */
bool _dbgflag_get(unsigned int idx);

/**
 * 把指定缓冲区数据拷贝到一个需要 32 位对齐的内存区域中
 * @param dst32 目标缓冲区。需要按 4 字节寻址，但起始地址、结束地址可以不按 4 字节对齐。
 * @param src 源缓冲区。假定无字节对齐要求（可以按 1 字节寻址）。
 * @param size 要拷贝的数据长度，可以是任意长度字节。
 * @return 拷贝的字节数
 */
size_t memcpy_to_aligned32(void *dst32, const void *src, size_t size);

/**
 * 把一个需要 32 位对齐的内存区域中的数据拷贝到指定缓冲区
 * @param dst 目标缓冲区。假定无字节对齐要求（可以按 1 字节寻址）。
 * @param src32 源缓冲区。需要按 4 字节寻址，但起始地址、结束地址可以不按 4 字节对齐。
 * @param size 要拷贝的数据长度，可以是任意长度字节。
 * @return 拷贝的字节数
 */
size_t memcpy_from_aligned32(void *dst, const void *src32, size_t size);
